#include "ServerSocket.h"

ServerSocket::ServerSocket() :
	port(atoi(Configuration::get_property("port").c_str())), buffer_size(atoi(
			Configuration::get_property("buffer_size").c_str())) {
	this->running = false;
}

void ServerSocket::run() {
	Log::log_oss << "Listening on port: " << port;
	Log::verbose();

	this->running = true;

	Log::verbose("Waiting for clients..");

	raw_socket_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP));
	if (raw_socket_fd == -1) {
		Log::error("Cannot create RAW socket");
		shutdown();
	}

	while (running) {
		proccess_packet();
	}
}

void ServerSocket::proccess_packet() {
	char* buffer = new char[buffer_size];
	const int bytes_read = read(raw_socket_fd, buffer, buffer_size);

	if (bytes_read > 0) {
		ip* ip_datagram = (ip*) (buffer + ETH_HLEN);
		if (ip_datagram->ip_p == IPPROTO_UDP) {
			udphdr* udp_segment = (udphdr*) (buffer + ETH_HLEN
					+ (ip_datagram->ip_hl << 2));

			if (ntohs(udp_segment->dest) == this->port) {
				Log::log_oss << "Client from "
						<< inet_ntoa(ip_datagram->ip_src);
				Log::verbose();

				ethhdr* ether_frame = (ethhdr*) (buffer);
				const int data_len = bytes_read - ETH_HLEN
						- (ip_datagram->ip_hl << 2) - sizeof(udphdr);

				string data_recv(buffer + bytes_read - data_len);
				data_recv.resize(data_len);

				Log::verbose(string("Message: ") + data_recv);

				std::vector<string> sv;
				unsigned int i = 0, j = data_recv.find(':', 0);
				while (j != string::npos) {
					sv.push_back(data_recv.substr(i, j - i));
					i = j + 1;
					j = data_recv.find(':', i);
				}
				sv.push_back(data_recv.substr(i));

				Message message(get_mac_address(ether_frame->h_source), sv[0],
						atoi(sv[1].c_str()));

				clientMessageHandler.enqueue_message(message);
				pthread_create(&thread_id, NULL, thread_router,
						(void*) &clientMessageHandler);
				pthread_join(thread_id, NULL);
			}
		}
	} else
		Log::warning("Cannot read from RAW socket");

	delete[] buffer;
}

string ServerSocket::get_mac_address(unsigned char* mac) {
	std::ostringstream oss;

	for (int i = 0; i < ETH_ALEN; i++) {
		oss << std::hex << std::uppercase << (mac[i] >> 4);
		oss << std::hex << std::uppercase << (mac[i] & 0x0f);
	}

	return oss.str();
}

void* ServerSocket::thread_router(void* cmh) {
	reinterpret_cast<ClientMessageHandler*> (cmh)->proccess_messages();
	return NULL;
}

void ServerSocket::shutdown() {
	running = false;
	close(raw_socket_fd);
	Log::verbose("Shutdowning");
}
